useSyncExternalStore
🧑🏻💻 useSyncExternalStore
useSyncExternalStore
는 외부 데이터 저장소를 구독할 수 있는 React 훅이다.
✅ useSyncExternalStore 문법
const snapshot = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot?)
subscribe
함수는 하나의callback
인수를 받아 데이터 저장소를 구독해야 하고, 구독 취소 함수를 반환해야 한다.getSnapshot
함수는 데이터 저장소에서 데이터의 스냅샷을 읽어야 한다.getServerSnapshot
는 데이터 저장소에 있는 데이터의 초기 스냅샷을 반환하는 함수이다.- React는 이 스냅샷를 사용해 컴포넌트가 스토어를 구독한 상태로 유지하고 변경 사항이 있을 때 다시 렌더링한다.
🧑🏻💻 참고하기
✅ useSyncExternalStore를 언제 사용해야 하는가?
- React 외부의 저장소에서 시간이 지남에 따라 변경되는 데이터를 읽어야 하는 경우
- 변이 가능한 값을 노출하는 브라우저 API와 그 변이 사항을 구독하는 이벤트를 사용할 때
- React 외부에 state를 보관하는 서드파티 상태 관리 라이브러리를 사용할 때
- 가능하면 React 빌트인 state는
useState
및useReducer
와 함께 사용하고 기존의 비 React 코드와 통합해야 할 때는useSyncExternalStore
를 사용하자.
✅ 선택형 인수인 getServerSnapshot
는 언제 사용해야 하는가?
- React 앱이 서버 렌더링을 사용하는 경우에 아래 상황이 발생할 때 사용한다.
→ 브라우저 전용 API에 연결하는 경우 (서버에 해당 API가 존재하지 않음)
→ 타사 데이터 저장소에 연결하는 경우 (서보와 클라이언트 간 데이터 일치 문제) - 참고로
getServerSnapshot
는 HTML을 생성할 때 서버에서 실행된다. 즉 React가 서버 HTML을 가져와서 인터랙티브하게 만들 때(hydration) 클라이언트에서 실행된다.
✅ 일반적인 사용 방법
import { useSyncExternalStore } from 'react';
import { todosStore } from './todoStore.js';
function TodosApp() {
const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
// ...
}
- 컴포넌트의 최상위 레벨에서 호출하여 외부 데이터 저장소에서 값을 읽는다.
- 사용자 정의 훅에서 호출하여 사용하면 기능 구현 반복을 막을 수 있다.
✅ 주의 사항
React 앱이 서버 렌더링을 사용하는 경우 이 함수가 제공되지 않으면 서버에서 컴포넌트를 렌더링할 때 오류가 발생한다.
스토어 데이터가 변이 가능한 경우
getSnapshot
함수는 해당 데이터의 불변 스냅샷을 반환해야 한다.리렌더링 시에 다른
subscribe
함수가 전달되면 React는 스토어를 다시 구독하는 문제가 있다. 이를 방지하기 위해서 컴포넌트 외부에서subscribe
를 선언하자.function ChatIndicator() {
const isOnline = useSyncExternalStore(subscribe, getSnapshot);
// ...
}
// ✅ 항상 동일한 함수이므로 React는 이를 재구독할 필요가 없습니다
function subscribe() {
// ...
}
🧑🏻💻 활용 및 생각할 거리
✅ 브라우저 API 구독하기 (ex. 네트워크 연결 상태)
import { useSyncExternalStore } from 'react';
function ChatIndicator() {
const isOnline = useSyncExternalStore(subscribe, getSnapshot);
return <h1>{isOnline ? '✅ Online' : '❌ Disconnected'}</h1>;
}
// snapShot 값 읽어어는 함수
// 브라우저 API에서 현재 값을 읽기
function getSnapshot() {
return navigator.onLine;
}
// 브라우저 API 데이터 저장소 구현
// 이벤트 구독 및 취소를 해주어야 한다.
function subscribe(callback) {
window.addEventListener('online', callback);
window.addEventListener('offline', callback);
return () => {
window.removeEventListener('online', callback);
window.removeEventListener('offline', callback);
};
}
✅ 외부 데이터 저장소가 업데이트 되면 리렌더링 하기
import { useSyncExternalStore } from 'react';
// 서드 파티 라이브러리에서 제공해주는 객체
import { todosStore } from './todoStore.js';
// 커스텀 훅
export default function TodosApp() {
const todos = useSyncExternalStore(todosStore.subscribe, todosStore.getSnapshot);
return (
<>
<button onClick={() => todosStore.addTodo()}>Add todo</button>
<hr />
<ul>
{todos.map(todo => (
<li key={todo.id}>{todo.text}</li>
))}
</ul>
</>
);
}